# -*- coding: utf-8 -*-

from memoriaFisica.paginacion.Pagina import Pagina
import threading

class Paginacion:
###############################################################################################################
    #INICIALIZACION
    lock = threading.RLock() # semaforo

    def __init__(self, tam,cantPaginas):
        self.tamanioPaginacion = tam
        self.paginasOcupadas =[]
        self.paginasVacias =[]
        self.crearPaginas(cantPaginas)
        self.mmu = None
        self.io = None
        self.tablaProgramListPaginas ={}
        self.dicProgramPagActual= {}
        self.programasCargados =[]
        
    def crearPaginas (self,x):
        n = 0
        while n<x:
            pag = Pagina(self.tamanioPaginacion)
            self.paginasVacias.append(pag)
            n +=1

###############################################################################################################
    #METODOS
    #REALIZAR ASIGNACION
    def realizarAsignacion(self, program):
        print("realizarAsignacion")
        if self.poseoNPaginasDisponibles(self.calcularPaginasNecesarias(program.lenCodigo())):
            self.cargarProgramaEnPaginas(program)
            print ("Se cargo correctamente el programa", program.getName(), "a memoria")
            print("memoria fisica",self.mmu.memoriaFisica.memory)
        else:
            print ("Quiero cargar el programa ", program.getName(), "no puedo, realizo swap")
            self.swap(program)
        
    def poseoNPaginasDisponibles(self, n):
        return len(self.paginasVacias)>=n
    
    def calcularPaginasNecesarias(self, lenPrograma):
        x= lenPrograma//self.tamanioPaginacion
        if (lenPrograma%self.tamanioPaginacion)!=0:
            x +=1
        return x
    
    def cargarProgramaEnPaginasAux(self, prog):
        n = 0
        desplazamiento = 0
        lista = []
        while n < self.calcularPaginasNecesarias(prog.lenCodigo()):
            pag = self.paginasVacias.pop(0)
            pag.cargarPagina(prog, desplazamiento)
            lista.append(pag)
            self.paginasOcupadas.append(pag)
            desplazamiento += pag.getTamanioPagina()
            n += 1
        return lista

    def cargarPrograma(self, prog):
        print('cargar programa en paginas')
        lista = self.cargarProgramaEnPaginasAux(prog)
        self.cargarPaginaEnMemoria(lista)
        elem = lista.pop(0) 
        self.dicProgramPagActual[prog] = elem
        self.tablaProgramListPaginas[prog]=lista
        self.programasCargados.append(prog)
        
    def cargarProgramaEnPaginas (self, aProgram):
        self.lock.acquire() # adquiero el semaforo, para evitar problemas en la ejecucion
        try:
            self.cargarPrograma(aProgram)
        finally:                                         
            self.lock.release() # libero el semaforo 
        
    def cargarPaginaEnMemoria(self, lista):
        for l in lista:
            self.mmu.cargarPaginaEnMemoria(l)
    
    #SWAP 
    def programasASacar (self, prog):
        listaPagsProgramaASacar = []
        listaPagsProgramaASacar.append(self.dicProgramPagActual[prog])
        paginasRestantes = self.tablaProgramListPaginas[prog]
        for p in paginasRestantes:
            listaPagsProgramaASacar.append(p)
        del self.dicProgramPagActual[prog]
        del self.tablaProgramListPaginas[prog]
        return listaPagsProgramaASacar
    
    def swap(self,prog):
        programASacar = self.programasCargados.pop(-1)#ToDo cambiar a elegirVictima
        print ("saco el programa", programASacar.getName(),"correctamente")
        self.io.agregar(programASacar)
        listaPagsProgramaASacar = self.programasASacar(programASacar)
        for i in listaPagsProgramaASacar:
            self.mmu.limpiarMemoriayTAblaDePaginas(i) 
        self.liberarPaginas(listaPagsProgramaASacar)
        self.realizarAsignacion(prog)
        
    def liberarPaginas(self, listaPags):
        for p in listaPags:
            self.paginasOcupadas.remove(p)
            p.miPagina = []
            self.paginasVacias.append(p)
    
    #GET instruccion
    def get(self, pcb):
        program = pcb.getProgram()
        if program in self.programasCargados:
            pag = self.dicProgramPagActual[program]
            if len(pag.miPagina)==0:
                lista = self.tablaProgramListPaginas[program]
                pag = lista.pop(0)
                self.dicProgramPagActual[program]= pag   
            return pag.miPagina.pop(0)
        else:
            self.io.eliminar(program)
            self.swap(program) 
            return self.get(program)
    
###############################################################################################################
    #GET&SET                  

    def getLibres(self):
        return self.Libres
    def setMMU (self, mmu):
        self.mmu = mmu
    def setIO (self, io):
        self.io = io